﻿using System;
using System.ComponentModel;
using System.Security.Principal;
using Tiscs.Practices.Diagnostics;

namespace Tiscs.Practices.Web.Services.Client
{
    /// <summary>
    /// 身份验证上下文。
    /// </summary>
    /// <typeparam name="T">用户信息类的类型。</typeparam>
    public class AuthenticationContext<T> : INotifyPropertyChanged where T : IPrincipal
    {
        /// <summary>
        /// 构造方法。
        /// </summary>
        /// <param name="client">身份验证客户端实例。</param>
        public AuthenticationContext(IAuthenticationServiceClient<T> client)
        {
            if (client == null)
            {
                throw new ArgumentNullException("client");
            }
            Client = client;
        }

        private T _user;

        /// <summary>
        /// 当前用户。
        /// </summary>
        public T User
        {
            get
            {
                return _user;
            }
            set
            {
                _user = value;
                RaisePropertyChanged("User");
                RaiseAuthenticationChanged();
            }
        }

        private bool _isBusy;

        /// <summary>
        /// 是否正在执行操作。
        /// </summary>
        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }
            private set
            {
                _isBusy = value;
                RaisePropertyChanged("IsBusy");
            }
        }

        /// <summary>
        /// 确定当前没有执行任何操作。如果有操作正在执行，则抛出异常。
        /// </summary>
        private void EnsureNotBusy()
        {
            if (IsBusy)
            {
                throw new InvalidOperationException();
            }
        }

        /// <summary>
        /// 身份验证客户端实例。
        /// </summary>
        internal IAuthenticationServiceClient<T> Client { get; set; }

        /// <summary>
        /// 开始身份验证操作。
        /// </summary>
        /// <typeparam name="TO">身份验证操作类型。</typeparam>
        /// <param name="callback">异步操作执行完毕回调。</param>
        /// <param name="creator">创建身份验证操作的方法。</param>
        /// <returns>身份验证操作实例。</returns>
        private TO StartOperation<TO>(Action<TO> callback, Func<Action<TO>, TO> creator) where TO : AuthenticationOperation<T>
        {
            EnsureNotBusy();
            TO operation = creator(op =>
                {
                    IsBusy = false;
                    User = op.User;
                    if (callback != null)
                    {
                        callback(op);
                    }
                });
            IsBusy = true;
            operation.StartInternal();
            return operation;
        }

        /// <summary>
        /// 用户登录。
        /// </summary>
        /// <param name="parameters">用户登录参数。</param>
        /// <param name="callback">异步执行回调。</param>
        /// <param name="userState">用户状态。</param>
        /// <returns>用户登录操作实例。</returns>
        public SignInOperation<T> SignInAsync(SignInParameters parameters, Action<SignInOperation<T>> callback, object userState = null)
        {
            return StartOperation<SignInOperation<T>>(callback, cb => new SignInOperation<T>(Client, parameters, cb, userState));
        }

        /// <summary>
        /// 注销登录。
        /// </summary>
        /// <param name="callback">异步执行回调。</param>
        /// <param name="userState">用户状态。</param>
        /// <returns>用户注销操作实例。</returns>
        public SignOutOperation<T> SignOutAsync(Action<SignOutOperation<T>> callback, object userState = null)
        {
            return StartOperation<SignOutOperation<T>>(callback, cb => new SignOutOperation<T>(Client, cb, userState));
        }

        /// <summary>
        /// 加载用户。
        /// </summary>
        /// <param name="callback">异步执行回调。</param>
        /// <param name="userState">用户状态。</param>
        /// <returns>加载用户操作实例。</returns>
        public LoadUserOperation<T> LoadUserAsync(Action<LoadUserOperation<T>> callback, object userState = null)
        {
            return StartOperation<LoadUserOperation<T>>(callback, cb => new LoadUserOperation<T>(Client, cb, userState));
        }

        /// <summary>
        /// 保存用户。
        /// </summary>
        /// <param name="callback">异步执行回调。</param>
        /// <param name="userState">用户状态。</param>
        /// <returns>保存用户操作实例。</returns>
        public SaveUserOperation<T> SaveUserAsync(Action<SaveUserOperation<T>> callback, object userState = null)
        {
            return StartOperation<SaveUserOperation<T>>(callback, cb => new SaveUserOperation<T>(Client, User, cb, userState));
        }

#if SERVER || DESKTOP

        /// <summary>
        /// 用户登录。
        /// </summary>
        /// <param name="parameters">用户登录参数。</param>
        public void SignIn(SignInParameters parameters)
        {
            Exception exception;
            if (!Validation.Parameter<SignInParameters>(nameof(parameters)).AssertNotNull().CheckRules(parameters, out exception))
            {
                throw exception;
            }
            User = Client.SignIn(parameters.UserName, parameters.Password, parameters.CustomData, parameters.IsPersistent);
        }

        /// <summary>
        /// 注销登录。
        /// </summary>
        public void SignOut()
        {
            User = Client.SignOut();
        }

        /// <summary>
        /// 加载用户。
        /// </summary>
        public void LoadUser()
        {
            User = Client.GetUser();
        }

        /// <summary>
        /// 保存用户。
        /// </summary>
        public void SaveUser()
        {
            Client.UpdateUser(User);
        }

#endif

        /// <summary>
        /// 触发属性变更事件。
        /// </summary>
        /// <param name="propertyName">发生变化的属性名称。</param>
        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// 继承自<see cref="INotifyPropertyChanged"/>接口的事件。
        /// 属性变化通知事件。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 触发用户认真状态变更事件。
        /// </summary>
        protected void RaiseAuthenticationChanged()
        {
            if (AuthenticationChanged != null)
            {
                AuthenticationChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 用户认证状态变更事件。
        /// </summary>
        public event EventHandler AuthenticationChanged;
    }
}
